

import { resolve } from 'path';
import schema from 'async-validator';
import *  as vm from 'vm';
import { format } from 'prettier';
import { AppContext } from 'ROOT/interface/App';
import KLineHistoryService from 'ROOT/module/kline-history/KLineHistory.service';
import StrategyService from 'ROOT/module/strategy/strategy.service';
import WatchEntityService from '../watch/watch.service';
import SymbolFactorService from '../symbol-factor/SymbolFactor.service'
import SymbolOrderConfigService from "../symbol-order-config/SymbolOrderConfig.service";
import { runWorker, transformToCSV } from 'ROOT/utils';
import { toStrategData } from './strategy.worker';
import { outLogger } from 'ROOT/common/logger';
import { sample } from 'ROOT/trader/Statistics';
import { Period, PeriodProcess, defaultStrategy } from 'ROOT/trader';
import { Cache } from 'ROOT/common/Cache';
import StrategyEntity from './strategy.entity';
import { trainMixIndicatorsTolerance, trainStrategy } from '../train/train';
import { AUTHORITY } from 'ROOT/constants/common';
import { traderRobot } from 'ROOT/TraderRobot';


const touristCache = new Cache<{
  time: number
  marks: string
  history: string
}>(1000 * 60 * 5)

export default class StrategyController {
  public static index = async (ctx: AppContext) => {
    const query = ctx.request.query as  {
      start: string;
      end: string;
      symbol: string;
      mode: string;
      period: Period;
      strategyId?: string;
    };
    const validator = new schema({
      start: {
        type: "string",
        required: true,
      },
      end: {
        type: "string",
        required: true,
      },
      symbol: {
        type: "string",
        required: true,
      },
      period: {
        type: 'string'
      },
      mode: {
        type: 'string',

      },
    });
    try {
      await validator.validate(query);
    } catch ({ errors, fields }) {
      if (errors) {
        return ctx.sendError({ errors });
      }
    }
    try {
      const userId = ctx.state.user && ctx.state.user.id;

      const history = await KLineHistoryService.find({
        start: Number(query.start),
        end: Number(query.end),
        symbol: (query.symbol).toLowerCase(),
      });
      if (history.length < 2) {
        ctx.sendError({ message: '数据不足' });
        return;
      }

      let strategyData: StrategyEntity;
      if (query.mode) {
        if (query.mode === "user") {
          const userSymbolOrderConfig = await SymbolOrderConfigService.findOne({ userid: Number(userId), symbol: query.symbol  });
          if (userSymbolOrderConfig.strategy) {
            strategyData = userSymbolOrderConfig.strategy
          }
          if (!strategyData) {
            outLogger.info(`${userId} no strategy, use default`);
          }
        } else if(query.mode === 'strategy' && query.strategyId) {
          strategyData = await StrategyService.findOne({id: Number(query.strategyId)});
        }
      }
      
      if (!strategyData) {
        const watchEntity = await WatchEntityService.findOne({symbol: query.symbol});
        if (watchEntity && watchEntity.strategy) {
          strategyData = watchEntity.strategy;
          outLogger.info(`使用默认 WatchEntityService ${query.symbol} strategy_id:${strategyData.id}`)
        } else {
          ctx.sendError({ message: `默认未绑定策略` });
          return;
        }
      }

      const { periodList, predictedData, marks } = await runWorker<typeof toStrategData>(resolve(__dirname, './strategy.worker.ts'), { 
        symbol: query.symbol,
        strategyStr: strategyData.strategy,
        period: query.period
      }, {
        workerData: {
          symbol: query.symbol,
          symbolVectors: sample.symbolVectors,
          history,
        }
      });

      ctx.sendSuccess({
        data: {
          marks: marks,
          history: periodList,
          predictedData: predictedData
        }
      });
      return;
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }

  public static tourist = async (ctx: AppContext) => {
    const { start, end, symbol, period } = ctx.request.query as {
      start: string;
      end: string;
      symbol: string;
      mode: string;
      period: Period;
    };
    const mode = ctx.request.query.mode
    const validator = new schema({
      start: {
        type: "string",
        required: true,
      },
      end: {
        type: "string",
        required: true,
      },
      symbol: {
        type: "string",
        required: true,
      },
      mode: {
        type: "string",
        required: true,
      }
    });
    try {
      await validator.validate({ start, end, symbol, mode });
    } catch ({ errors, fields }) {
      if (errors) {
        return ctx.sendError({ errors });
      }
    }
    const key = `${period ? period : ''}${mode}${symbol}${start}`
    try {
      if (touristCache.get(key)) {
        ctx.sendSuccess({
          data: {
            marks: touristCache.get(key).marks,
            history: touristCache.get(key).history,
          }
        });
        return; 
      }
      const history = await KLineHistoryService.find({
        start: Number(start),
        end: Number(end),
        symbol: symbol.toLowerCase()
      });
      const watchEntity = await WatchEntityService.findOne({symbol: symbol});
      if (history.length < 2) {
        ctx.sendError({ message: '数据不足' });
        return;
      }
 
      const { periodList, predictedData, marks } = await runWorker<typeof toStrategData>(resolve(__dirname, './strategy.worker.ts'), {
        symbol: symbol,
        strategyStr: watchEntity.strategy.strategy,
 
        period: period as Period
      }, {
        workerData: {
          symbol: symbol,
          symbolVectors: sample.symbolVectors,
          history,
        }
      })

      touristCache.set(key, {
        time: Date.now(),
        history: periodList,
        marks: marks
      })
      ctx.sendSuccess({
        data: {
          marks: marks,
          history: periodList,
          predictedData,
        }
      });
      return;
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static updateUserSymbolStrategyByAdmin= async (ctx: AppContext) => {
    const body = ctx.request.body as {
      symbol_order_config_id: number;
      strategy_id: number
    };
    try {
      const symbolOrderConfig = await SymbolOrderConfigService.findOne({id: body.symbol_order_config_id});
      if (!symbolOrderConfig) {
        ctx.sendError({ message: ` SymbolOrderConfigService.findOne:${body.symbol_order_config_id}` });
        return;
      }
      const strategyData = await StrategyService.findOne({id: body.strategy_id});
      if (!strategyData && body.strategy_id !== 0) {
        ctx.sendError({ message: `body.strategy_id: ${body.strategy_id}` });
        return;
      }
      symbolOrderConfig.strategy = body.strategy_id === 0 ? null : strategyData;
      symbolOrderConfig.strategy_id = body.strategy_id === 0 ? null : strategyData.id;
      await SymbolOrderConfigService.repository.save(symbolOrderConfig);
      traderRobot.updateOrderConfig(symbolOrderConfig);
      ctx.sendSuccess({ data: symbolOrderConfig});
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static getUserSymbolStrategyList = async (ctx: AppContext) => {
    const query = ctx.request.query as Record<string, any>;
    const userId = ctx.state.user && ctx.state.user.id;
    try {
      // if (query.symbol) {

      // } else if (query.order_id){
      //   const symbolOrderConfig = await SymbolOrderConfigService.findOne({ id: query.order_id});
      //   if (!symbolOrderConfig) {
      //     ctx.sendError({ message: 'order_id not found' });
      //     return;
      //   }
      //   const defaultList = await StrategyService.find({symbol: symbolOrderConfig.symbol, userid: 0});
      //   const onwerList = await StrategyService.find({symbol: symbolOrderConfig.symbol, userid: userId});
      //   onwerList.forEach(item => {
      //     Object.assign(item, {editable: true})
      //   });
      //   ctx.sendSuccess({ data: [...defaultList, ...onwerList]});
      // }
      const symbolOrderConfig = await SymbolOrderConfigService.find({symbol: query.symbol});

      return ctx.sendSuccess({ data: symbolOrderConfig});

    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static getAllSymbolStrategyList = async (ctx: AppContext) => {
    const query = ctx.request.query as Record<string, any>;
    try {
      const list = await StrategyService.find({symbol: query.symbol});
      ctx.sendSuccess({ data: [...list]});
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static getAllStrategyList = async (ctx: AppContext) => {
    try {
      const list = await StrategyService.find({});
      ctx.sendSuccess({ data: [...list]});
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static strategyFunction = async (ctx: AppContext) => {
    const query = ctx.request.query as Record<string, any>;
    const userId = ctx.state.user && ctx.state.user.id;
    // const validator = new schema({
    //   symbol: {
    //     type: "string",
    //     required: true,
    //   },
    //   strategy_id: {
    //     type: "string",
    //     required: true,
    //   }
    // });
    // try {
    //   await validator.validate(query);
    // } catch ({ errors, fields }) {
    //   ctx.sendError({ errors });
    //   return;
    // }
    try {

      if (query.strategy_id) {
        const res = await StrategyService.findOne({id: query.strategy_id, userid: userId});
        ctx.sendSuccess({ data: res }); 
      } else if (query.symbol) {
        const res = await WatchEntityService.findOne({symbol: query.symbol});
        ctx.sendSuccess({ data: res }); 
      } else {
        ctx.sendError({ message: `required parameter missing, strategy_id or symbol` });
      }
    } catch (error) {
      ctx.sendError({ message: error });
    }
  }
  public static defaultStrategyFunction = async (ctx: AppContext) => {
    try {
      ctx.sendSuccess({ data: format(defaultStrategy(), { parser: 'typescript' })}); 
    } catch (error) {
      ctx.sendError({ message: error });
    }
  }
  

  public static updateStrategyFunction = async (ctx: AppContext) => {
    const data = ctx.request.body as StrategyEntity & {_id?: string, userid?: number};
    const userId = ctx.state.user && ctx.state.user.id;
    
    try {
      for (const strategyData of [data]) {   

        if (strategyData.id && strategyData.userid && strategyData.userid === userId) {
          await StrategyService.update({id: strategyData.id, userid: userId}, strategyData);
          traderRobot.trader.udpateCustomStrategy(strategyData.id, strategyData.symbol, {
            code: strategyData.strategy
          });
        } else if (strategyData._id) {
          await StrategyService.create({...strategyData, userid: userId});
        } else if (ctx.state.user?.role == AUTHORITY.admin && strategyData.id) {
          await StrategyService.updateByAdmin({id: strategyData.id}, strategyData);
          traderRobot.trader.udpateCustomStrategy(strategyData.id, strategyData.symbol, {
            code: strategyData.strategy
          });
        } 
      }
      ctx.sendSuccess({ data: {} });
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }

  public static deleteStrategyFunction = async (ctx: AppContext) => {
    const data = ctx.request.body as {id: number};
    try {
      await StrategyService.deleteOne({id: data.id});
      ctx.sendSuccess({ data: {} });
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
  public static train = async (ctx: AppContext) => {
    const data = ctx.request.body as {symbol: string, start: string, end: string, type: string};
    
    const validator = new schema({
      start: {
        type: "string",
        required: true,
      },
      end: {
        type: "string",
        required: true,
      },
      symbol: {
        type: "string",
        required: true,
      },
      type: {
        type: "string",
      },
    });
    try {
      await validator.validate(data);
    } catch ({ errors, fields }) {
      if (errors) {
        return ctx.sendError({ errors });
      }
    }
    try {
      const [ symbolFactor, history ] = await Promise.all([
          SymbolFactorService.find({symbol: data.symbol, period: '4h'}),
          KLineHistoryService.find({symbol: data.symbol, start: new Date(data.start).getTime(), end: new Date(data.end).getTime()})
      ]);

 
      if (data.type === 'short') {
        if (history.length === 0) {
          ctx.sendError({ message: '数据不足' });
          return;
        }
        trainStrategy(history);
        ctx.sendSuccess({ data: {}});
        return
      } else {
        if (symbolFactor.length === 0 || history.length === 0) {
          ctx.sendError({ message: '数据不足' });
          return;
        }
        trainMixIndicatorsTolerance(symbolFactor, history);
      }

    
      ctx.sendSuccess({ data: {} });
    } catch (error) {
      ctx.sendError({ message: error.message });
    }
  }
}

